package org.bdware.registry.webService.service;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import org.apache.log4j.Logger;
import org.bdware.doip.core.doipMessage.DoipMessage;
import org.bdware.doip.core.doipMessage.DoipMessageFactory;
import org.bdware.doip.core.doipMessage.DoipResponseCode;
import org.bdware.doip.core.doipMessage.MessageCredential;
import org.bdware.doip.core.model.digitalObject.DigitalObject;
import org.bdware.doip.core.model.metadata.MetaDO;
import org.bdware.doip.core.model.metadata.SearchParameter;
import org.bdware.doip.core.model.operations.BasicOperations;
import org.bdware.doip.endpoint.doipServer.DoipServiceInfo;
import org.bdware.registry.handler.BDRegistryHandler;
import org.bdware.registry.main.BDRegistry;
import org.bdware.registry.storage.BDRegStorage;
import org.bdware.registry.webService.dto.Stat;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class MetaDOService {
    Logger logger = Logger.getLogger(MetaDOService.class);

    BDRegStorage store = BDRegistry.getStore();
    BDRegistryHandler handler = BDRegistry.getRegistryHandler();
    DoipServiceInfo doipInfo = BDRegistry.getInfo();

    public MetaDOService() throws InterruptedException {
        int count = 0;
        while(handler == null || doipInfo == null){
            logger.info("wait for DOIP Service initialization");
            Thread.sleep(1000);
            if(++count>5) break;
        }
        if(handler == null || doipInfo == null){
            logger.error("DOIP Service not initialized.");
        }
    }

    public String storeDO(DigitalObject digitalObject, String userID){
        DoipMessage msg =
                new DoipMessageFactory.DoipMessageBuilder()
                        .createRequest(doipInfo.id, BasicOperations.Create.getName())
                        .setBody(digitalObject)
                        .create();
        msg.credential = new MessageCredential(userID);
        DoipMessage resp = handler.handleCreate(msg);
        if(resp.header.parameters.response != DoipResponseCode.Success){
            logger.info("create DO failed: " + resp.header.parameters.response.getName());
            logger.info("msg body: " + resp.body.getDataAsJsonString());
            return resp.header.parameters.response.getName();
        }
        try {
            logger.info("create DO: " + resp.body.getDataAsDigitalObject().toString());
            return resp.body.getDataAsDigitalObject().toString();
        }catch (Exception e){
            logger.info("create DO: " + resp.body.getDataAsJsonString());
            return resp.body.getDataAsJsonString();
        }
    }

    public String updateDO(DigitalObject digitalObject, String userID){
        DoipMessage msg =
                new DoipMessageFactory.DoipMessageBuilder()
                        .createRequest(doipInfo.id, BasicOperations.Update.getName())
                        .setBody(digitalObject)
                        .create();
        msg.credential = new MessageCredential(userID);
        DoipMessage resp = handler.handleUpdate(msg);
        if(resp.header.parameters.response != DoipResponseCode.Success){
            logger.info("update DO failed: " + resp.header.parameters.response.getName());
            logger.info("msg body: " + resp.body.getDataAsJsonString());
            return resp.header.parameters.response.getName();
        }
        try {
            logger.info("update DO: " + resp.body.getDataAsDigitalObject().toString());
            return resp.body.getDataAsDigitalObject().toString();
        }catch (Exception e){
            logger.info("update DO: " + resp.body.getDataAsJsonString());
            return resp.body.getDataAsJsonString();
        }
    }

    public JsonArray getDOListByUserID(String userID){
        return store.getDOsByUserID(userID);
    }

    public HashMap<String,JsonArray> getAllDOListGroupByUserID(){
        return store.getUserStats();
    }

    public String getHottestMetaDO(int size){
        HashMap<String,Integer> doStats = store.getAccessStats();
        TreeSet<Map.Entry<String,Integer>> entrySet = new TreeSet<>(Comparator.comparingInt(Map.Entry::getValue));
        entrySet.addAll(doStats.entrySet());
        if(entrySet.size() < size)
            size = entrySet.size();

        ArrayList<MetaDOAbstract> metaList = new ArrayList<>();
        Iterator<Map.Entry<String,Integer>> it = entrySet.descendingIterator();
        while(metaList.size() < size && it.hasNext()){
            Map.Entry<String,Integer> e = it.next();
            MetaDO meta = MetaDO.fromDO(store.getDoByID(e.getKey()));
            if(meta == null) continue;
            MetaDOAbstract mda = new MetaDOAbstract(meta.id,meta.getMetadata("title"),e.getValue());
            metaList.add(mda);
        }
        return new Gson().toJson(metaList);
    }

    public DigitalObject getDOByID(String doID){
        return store.getDoByID(doID);
    }

    public boolean deleteDOByID(String doID,String userID){
        DoipMessage msg =
                new DoipMessageFactory.DoipMessageBuilder()
                        .createRequest(doID,BasicOperations.Delete.getName())
                        .create();
        msg.credential = new MessageCredential(userID);
        DoipMessage resp = handler.handleDelete(msg);
        if(resp.header.parameters.response != DoipResponseCode.Success){
            logger.info("create DO failed: " + resp.header.parameters.response.getName());
            logger.info("msg body: " + resp.body.getDataAsJsonString());
            return false;
        }else {
            logger.info("delete success: " + doID);
            logger.info("user: " + userID);
            return true;
        }
    }

    public String search(String query, int size){
        SearchParameter sp = SearchParameter.getDefaultParameter();
        sp.query = query;
        sp.type = "full";
        sp.pageNum = 1;
        sp.pageSize = size;
        DoipMessage msg =
                new DoipMessageFactory.DoipMessageBuilder()
                        .createRequest(doipInfo.id,BasicOperations.Search.getName())
                        .addAttributes("query",sp.query)
                        .addAttributes("pageNum",sp.pageNum)
                        .addAttributes("pageSize",sp.pageSize)
                        .addAttributes("type",sp.type)
                        .addAttributes("sortFields",sp.sortFieldsSer)
                        .create();
        DoipMessage resp = handler.handleSearch(msg);
        return resp.body.getDataAsJsonString();
    }

    static class MetaDOAbstract{
        String id;
        String title;
        int accessTimes;
        public MetaDOAbstract(String i,String t,int a){
            id = i;
            title = t;
            accessTimes = a;
        }
    }

    /*
    返回registry的统计信息
     */
    public Stat getStat(){
        return store.getStat();
    }
}
